#pragma once

#include "Sem.hpp"

#include <iostream>
#include <vector>

const int DEFAULT_CAPACITY = 5;

template<class T>
class RingQueue
{
public:
    RingQueue(int capacity = DEFAULT_CAPACITY)
        : _rq(capacity) // 给顺序表开空间
        , _capacity(capacity) // 设置容量大小
        , _proIndex(0) // 生产者下标
        , _conIndex(0) // 消费者下标
        , _spaceSem(capacity) // 空间信号量，初始值为就是队列大小
        , _dataSem(0) // 数据信号量，初始值为0
    {
        // 构造的时候对两把锁进行初始化
        pthread_mutex_init(&_proMtx, nullptr);
        pthread_mutex_init(&_conMtx, nullptr);
    }

    // 生产者生产
    void Push(const T& data)
    {
        // 生产前申请信号量，spaceSem减少
        _spaceSem.P();

        // 多个生产者一同Push的时候要上锁，让一个生产者生产
        pthread_mutex_lock(&_proMtx);

        // 申请完信号量后就进行生产
        _rq[_proIndex++] = data;
        _proIndex %= _capacity;

        // 单个生产者生产完毕后解锁
        pthread_mutex_unlock(&_proMtx);    

        // spaceSem减少，dataSem增加        
        _dataSem.V();
    }

    // 消费者消费
    void Pop(T& data)
    {
        // 消费前先申请信号量，dataSem减少
        _dataSem.P();
        
        // 多个消费者一同Pop的时候要上锁，让一个消费者消费
        pthread_mutex_lock(&_conMtx);

        // 申请完信号量后就进行消费
        data = _rq[_conIndex++];
        _conIndex %= _capacity;

        // 单个消费者消费完毕，解锁
        pthread_mutex_unlock(&_conMtx);

        // dataSem减少，spaceSem增加  
        _spaceSem.V();
    }

    ~RingQueue()
    {
        // 析构的时候记得释放锁
        pthread_mutex_destroy(&_proMtx);
        pthread_mutex_destroy(&_conMtx);
    }

private:
    // 物理结构的循环队列
    std::vector<T> _rq;
    // 队列大小
    int _capacity;
    // 生产者下标
    int _proIndex;
    // 消费者下标
    int _conIndex;
    // 生产者所需信号量
    Sem _spaceSem;
    // 消费者所需信号量
    Sem _dataSem;
    // 所有生产者的锁
    pthread_mutex_t _proMtx;
    // 所有消费者的锁
    pthread_mutex_t _conMtx;
};
